/**
 * CCLIOT库函数二次封装
 * 文件名称：	IOTNetAPI.java
 * 文件版本：	V10
 * 修改日期：	2017-04-24
 */
package com.ccl.iot;

import android.annotation.SuppressLint;
import android.util.Log;

import com.ccl.iot.IOTDataType.IOTBoolean;
import com.ccl.iot.IOTDataType.IOTInt;
import com.ccl.iot.IOTDataType.IOTLong;
import com.ccl.iot.IOTDataType.IOTShort;
import com.ccl.iot.IOTDataType.IOTString;
import com.ccl.iot.IOTDataType.IOTULong;
import com.ccl.iot.object.IOTAuthInfo;
import com.ccl.iot.object.IOTBatchInfo;
import com.ccl.iot.object.IOTBatchItemInfo;
import com.ccl.iot.object.IOTDeviceInfoEx;
import com.ccl.iot.object.IOTDeviceTrigger;
import com.ccl.iot.object.IOTInfoWithValue;
import com.ccl.iot.object.IOTRegLog;
import com.ccl.iot.object.IOTTime;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;

public class IOTNetAPI extends IOTNetAPINative {
    /*
     * 	Static functions
     */
    public static final int KAPI_CACHE_SIZE = 1024000;

    public static final int KAPI_STATE_NONE = 0;
    public static final int KAPI_STATE_INITIALIZING = 1;
    public static final int KAPI_STATE_INITIALIZED = 2;
    public static final int KAPI_STATE_CLEANING = 3;

    private static IOTNetAPI iInstance = null;

    public static void Initialize(String aDBPath, IOTCallback.ErrorCallback aErrorCallback) {
        if (iInstance == null) {
            iInstance = new IOTNetAPI(aDBPath, aErrorCallback);
        }
    }

    public static IOTNetAPI GetInstance() {
        return iInstance;
    }

    public static int GetVersion() {
        return IOTGetVersion();
    }

    public static String GetStringVersion() {
        int tVersion = IOTGetVersion();

        return ((tVersion >> 24) & 0xFF) + "." + ((tVersion >> 16) & 0xFF) + ((tVersion >> 8) & 0xFF) + (tVersion & 0xFF);
    }

    public static long GetDatabaseFileSize() {
        if (iInstance != null) {
            return iInstance.getDatabaseFileSize();
        }

        return 0;
    }

    public static boolean RestoreDatabase() {
        if (iInstance != null) {
            return iInstance.restoreDatabase();
        }

        return false;
    }


    /*
     * 	Non-static functions
     */
    private IOTCallback.ErrorCallback iErrorCallback = null;

    private Object iSyncLock = new Object();
    private volatile int iState = KAPI_STATE_NONE;

    private volatile boolean iWaitingLogout = false;
    private Thread iNetThread = null;
    private Object iPeriodLock = new Object();

    private byte iCache[] = null;//new byte[KAPI_CACHE_SIZE];


    private boolean isOK(String aAction, int aResult) {
        if (EIOT_RESULT.ISOK(aResult)) {
            return true;
        } else if (iErrorCallback != null) {
            iErrorCallback.onError(aAction, aResult);
        }

        return false;
    }

    private IOTNetAPI(String aDBPath, IOTCallback.ErrorCallback aErrorCallback) {
        boolean tInited = false;

        iErrorCallback = aErrorCallback;

        if (aDBPath != null) {
            File tFile = new File(aDBPath);
            byte tPath[] = aDBPath.getBytes();
            byte tDBPath[] = new byte[tPath.length + 1];

            if (!tFile.exists()) {
                if (!tFile.mkdirs()) {
                    return;
                }
            }

            tDBPath[tDBPath.length - 1] = 0;
            System.arraycopy(tPath, 0, tDBPath, 0, tPath.length);

            tInited = ISTRUE(IOTInit(tDBPath, iCache, iCache != null ? iCache.length : 0));
        } else {
            tInited = ISTRUE(IOTInit(null, iCache, iCache != null ? iCache.length : 0));
        }

        if (!tInited) {
            isOK("Init", EIOT_RESULT.EIOT_RESULT_UNKNOWN);
        }
    }

    /*
     * Smart connect functions
     */
    private Object iSMConnSyncLock = new Object();
    private volatile boolean iSMConnRunning = false;

    public boolean startSmartConnect(String aSSID, String aPwd) {
        synchronized (iSMConnSyncLock) {
            if (aSSID != null) {
                if (iSMConnRunning == false) {
                    byte tSSID[] = aSSID.getBytes();
                    byte tPwd[] = null;

                    if (aPwd != null) {
                        tPwd = aPwd.getBytes();
                    }

                    if (EIOT_RESULT.ISOK(IOTSMConnStart(0, tSSID, tSSID.length, tPwd, tPwd == null ? 0 : tPwd.length, FALSE))) {
                        iSMConnRunning = true;

                        return true;
                    }
                }
            }
        }

        return false;
    }

    public boolean stopSmartConnect() {
        synchronized (iSMConnSyncLock) {
            if (iSMConnRunning) {
                IOTSMConnStop(0);
                iSMConnRunning = false;
            }
        }

        return true;
    }

    /*
     * Base API functions
     */
    public long getDatabaseFileSize() {
        return IOTGetDBFileSize();
    }

    public boolean restoreDatabase() {
        if (isOK("RestoreDatabase", IOTRestoreDB())) {
            iUpdatedDeviceInfoList = null;
            iUpdatedPublicUserInfoListUser = KINVALID_USER_ID;

            return true;
        }

        return false;
    }

    private IOTCallback.LoginResultCallback iLoginResultCallbackEx = null;

    public void setLoginResultCallback(IOTCallback.LoginResultCallback aCallback) {
        iLoginResultCallbackEx = aCallback;
    }

    private IOTCallback.LoginResultCallback iLoginResultCallback = new IOTCallback.LoginResultCallback() {
        @Override
        public void onLoginResultCallback(int aResult) {
            if (iLoginResultCallbackEx != null) {
                iLoginResultCallbackEx.onLoginResultCallback(aResult);
            }
        }
    };

    private IOTCallback.NotifyCallback iNotifyCallbackEx = null;

    public void setNotifyCallback(IOTCallback.NotifyCallback aCallback) {
        iNotifyCallbackEx = aCallback;
    }

    private IOTCallback.NotifyCallback iNotifyCallback = new IOTCallback.NotifyCallback() {
        @Override
        public void onNotifyCallback(int aNotifyType, long aRelativeUser, long aRelativeDevice, long aCreateTime,
                                     int aContentSize, byte[] aContent) {
            if (iNotifyCallbackEx != null) {
                iNotifyCallbackEx.onNotifyCallback(aNotifyType, aRelativeUser, aRelativeDevice, aCreateTime, aContentSize, aContent);
            }
        }
    };

    public void threadLock() {
        IOTThreadLock();
    }

    public void threadUnlock() {
        IOTThreadUnlock();
    }

    public boolean isStarted(boolean aExcludeOneshot) {
        return !iWaitingLogout && (iState == KAPI_STATE_INITIALIZED || (!aExcludeOneshot && IOTOneshotReady() != 0));
    }

    public boolean isStarted() {
        return isStarted(false);
    }

    public boolean startUp() {
        if (iState == KAPI_STATE_NONE || iState == KAPI_STATE_CLEANING) {
            while (iState == KAPI_STATE_CLEANING)
                ;

            synchronized (iSyncLock) {
                if (iNetThread == null) {
                    iState = KAPI_STATE_INITIALIZING;
                } else {
                    return false;
                }
            }

            iNetThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    Thread.currentThread().setName("IOTNetAPI main thread");

                    if (iState == KAPI_STATE_INITIALIZING) {
                        IOTSetLoginResultCallBack(iLoginResultCallback);
                        IOTSetNotifyCallBack(iNotifyCallback);
                        IOTSetDeviceInfoCallBack(iDeviceInfoCallback);
                        IOTSetAuthStateCallBack(iAuthStateCallback);
                        IOTSetRegDataCallBack(iRegDataCallback);

                        synchronized (iSyncLock) {
                            if (iState == KAPI_STATE_INITIALIZING) {
                                int i = IOTStartup();
                                isOK("Start up", i);

                                iState = KAPI_STATE_INITIALIZED;
                            }
                        }

                        while (iState == KAPI_STATE_INITIALIZED) {
                            if (iState == KAPI_STATE_INITIALIZED) {
                                if (!iWaitingLogout) {
                                    if (ISFALSE(IOTPeriod())) {
                                        break;
                                    }
                                }
                            } else {
                                break;
                            }

                            if (iState == KAPI_STATE_INITIALIZED) {
                                try {
                                    synchronized (iPeriodLock) {
                                        iPeriodLock.wait(100);
                                    }
                                } catch (InterruptedException e) {
                                    break;
                                }
                            }
                        }

                        synchronized (iSyncLock) {
                            IOTShutdown();
                        }
                    }

                    Log.i("IOTNetAPI", "IOTNetAPI main thread exit");

                    synchronized (iSyncLock) {
                        iState = KAPI_STATE_NONE;

                        iNetThread = null;
                    }
                }
            });

            iNetThread.start();

            while (iState == KAPI_STATE_INITIALIZING) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    return false;
                }
            }

            return true;
        }

        return false;
    }

    public void shutdown() {
        if (iState == KAPI_STATE_INITIALIZING || iState == KAPI_STATE_INITIALIZED) {
            iState = KAPI_STATE_CLEANING;

            synchronized (iPeriodLock) {
                iPeriodLock.notify();
            }
        }
    }

    public boolean startOneshot() {
        if (!isStarted() && getLogonUser() != KINVALID_USER_ID) {
            return isOK("StartOneshot", IOTStartOneshot());
        }

        return false;
    }

    public boolean isOneshotReady() {
        return ISTRUE(IOTOneshotReady());
    }

    public boolean isServerDisabled() {
        return ISTRUE(IOTIsServerDisabled());
    }

    public void setServerDisabled(boolean aDisable) {
        IOTSetServerDisabled(aDisable ? TRUE : FALSE);
    }

    public boolean isBroadcastEnabled() {
        return ISTRUE(IOTIsBroadcastEnabled());
    }

    public void setBroadcastEnabled(boolean aEnabled) {
        IOTSetBroadcastEnabled(aEnabled ? TRUE : FALSE);
    }

    public boolean isLocalReady() {
        return ISTRUE(IOTLocalReady());
    }

    public boolean isRemoteReady() {
        return ISTRUE(IOTRemoteReady());
    }

    public long getDeviceID() {
        return IOTGetDeviceID();
    }

    /*
     * Mix IOT functions
     */
    public boolean checkDeviceOnline(long aDevID, IOTBoolean aOnline, int aNetwork) {
        if (isStarted() && aOnline != null) {
            int tOnline[] = {FALSE};

            if (isOK("CheckDeviceOnline", IOTCheckDeviceOnline(aDevID, tOnline, 0, aNetwork))) {
                aOnline.setValue(ISTRUE(tOnline[0]));

                return true;
            }
        }

        return false;
    }

    public boolean checkDeviceOnline(long aDevID, IOTBoolean aOnline) {
        return checkDeviceOnline(aDevID, aOnline, EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_BOTH);
    }

    private IOTCallback.DeviceInfoCallback iDeviceInfoCallbackEx = null;

    public void setDeviceInfoCallback(IOTCallback.DeviceInfoCallback aCallback) {
        iDeviceInfoCallbackEx = aCallback;
    }

    private IOTCallback.DeviceInfoCallback iDeviceInfoCallback = new IOTCallback.DeviceInfoCallback() {
        @Override
        public void onDeviceInfoCallback(long aDevID, short aType, short aModel, int aVersion, int aRegisterSpace, int aDeviceIP) {
            if (iDeviceInfoCallbackEx != null) {
                iDeviceInfoCallbackEx.onDeviceInfoCallback(aDevID, aType, aModel, aVersion, aRegisterSpace, aDeviceIP);
            }
        }
    };

    private IOTCallback.AuthStateCallback iAuthStateCallbackEx = null;

    public void setAuthStateCallback(IOTCallback.AuthStateCallback aCallback) {
        iAuthStateCallbackEx = aCallback;
    }

    private IOTCallback.AuthStateCallback iAuthStateCallback = new IOTCallback.AuthStateCallback() {
        @Override
        public void onAuthStateCallback(int aNetWork, long aDevID, int aNewAuth) {
            if (iAuthStateCallbackEx != null) {
                iAuthStateCallbackEx.onAuthStateCallback(aNetWork, aDevID, aNewAuth);
            }
        }
    };

    public boolean authCheck(long aDevID, IOTInt aAuthState, int aNetwork) {
        if (isStarted() && aAuthState != null) {
            int tAuthState[] = {EAUTH_STATE.EAUTH_STATE_UNKNOWN};

            if (isOK("AuthCheck", IOTAuthCheck(aDevID, tAuthState, 0, aNetwork))) {
                aAuthState.setValue(tAuthState[0]);

                return true;
            }
        }

        return false;
    }

    public boolean authCheck(long aDevID, IOTInt aAuthState) {
        return authCheck(aDevID, aAuthState, EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_BOTH);
    }

    private IOTCallback.RegDataCallback iRegDataCallbackEx = null;

    public void setRegDataCallback(IOTCallback.RegDataCallback aCallback) {
        iRegDataCallbackEx = aCallback;
    }

    private IOTCallback.RegDataCallback iRegDataCallback = new IOTCallback.RegDataCallback() {
        @Override
        public void onRegDataCallback(int aNetWork, long aDevID, short aAddress, short aSize, byte[] aData,
                                      long aTime) {
            // Update register data to each device
//            Log.e("RegDataCallBack", "Call back [" + (aNetWork == EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_LOCAL ? "LOCAL" : "REMOTE") + "]:" + aDevID + ", " + aAddress + ", " + aSize + ", " + Arrays.toString(Arrays.copyOf(aData, aSize)) + ", time: " + aTime);

            if (iRegDataCallbackEx != null) {
                iRegDataCallbackEx.onRegDataCallback(aNetWork, aDevID, aAddress, aSize, aData, aTime);
            }
        }
    };

    public boolean readReg(long aDevID, short aAddress, IOTShort aSize, byte aData[], int aNetwork) {
        if (isStarted() && aSize != null && aData != null && aData.length > 0) {
            short tSize[] = {IOTDataType.C16(aData.length)};
            int i = IOTReadReg(aDevID, aAddress, tSize, aData, 0, aNetwork);
            if (isOK("ReadReg", i)) {
                aSize.setValue(tSize[0]);

                return true;
            }
        }
        return false;
    }

    public boolean readReg(long aDevID, short aAddress, IOTShort aSize, byte aData[]) {
        return readReg(aDevID, aAddress, aSize, aData, EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_BOTH);
    }

    public boolean writeReg(long aDevID, short aAddress, short aSize, byte aData[], long aAuthID, int aNetwork, boolean aIgnoreResponse) {
        if (isStarted() && aSize > 0 && aData != null && aData.length >= aSize) {
            int tRet = IOTWriteReg(aDevID, aAddress, aSize, aData, aAuthID, aNetwork, aIgnoreResponse ? TRUE : FALSE);
            if (tRet == EIOT_RESULT.EIOT_RESULT_GENERAL_REGISTER_NO_CHANGE) {
                iRegDataCallback.onRegDataCallback(aNetwork, aDevID, aAddress, aSize, aData, aAuthID);
            }

            return isOK("WriteReg", tRet);
        }

        return false;
    }

    public boolean writeReg(long aDevID, short aAddress, short aSize, byte aData[], int aNetwork) {
        return writeReg(aDevID, aAddress, aSize, aData, 0, aNetwork, aNetwork == EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_LOCAL ? true : false);
    }

    public boolean writeReg(long aDevID, short aAddress, short aSize, byte aData[]) {
        return writeReg(aDevID, aAddress, aSize, aData, 0, EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_BOTH, false);
    }

    /*
     * Local IOT functions
     */
    public boolean queryAddress(long aDevID, IOTCallback.AddressInfoCallback aCallback) {
        return isOK("QueryAddress", IOTQueryAddress(aDevID, aCallback));
    }

    public int startBeacon(byte aMAC[], int aInterval, IOTCallback.BeaconCallback aCallback) {
        if (isStarted() && aMAC != null && aMAC.length == 6 && aCallback != null) {
            int tRet = IOTStartBeacon(aMAC, aInterval, FALSE, aCallback);

            if (tRet >= 0 && tRet < 8) {
                return tRet;
            } else {
                isOK("StartBeacon", tRet);

                return tRet;
            }
        }

        return -1;
    }

    public int startBeacon(String aMAC, int aInterval, IOTCallback.BeaconCallback aCallback) {
        if (aMAC != null) {
            String tSMAC[] = aMAC.split(":");

            if (tSMAC != null && tSMAC.length == 6) {
                byte tMAC[] = new byte[6];

                for (int i = 0; i < 6; i++) {
                    tMAC[i] = Integer.valueOf(tSMAC[i], 16).byteValue();
                }

                return startBeacon(tMAC, aInterval, aCallback);
            }
        }

        return -1;
    }

    public boolean stopBeacon(int aIndex) {
        return isOK("StopBeacon", IOTStopBeacon(aIndex));
    }

    public boolean scanDevice() {
        if (isStarted()) {
            return isOK("ScanDevice", IOTScanDevice());
        } else {
            return false;
        }
    }

    public boolean resetDevice(long aDevID) {
        if (isStarted()) {
            int tRet;

            while ((tRet = IOTRequestSystemReset(aDevID)) == EIOT_RESULT.EIOT_RESULT_GENERAL_TRY_AGAIN)
                ;

            return isOK("SystemReset", tRet);
        }

        return false;
    }

    public boolean restoreDevice(long aDevID) {
        if (isStarted()) {
            int tRet;

            while ((tRet = IOTRequestSystemRestore(aDevID)) == EIOT_RESULT.EIOT_RESULT_GENERAL_TRY_AGAIN)
                ;

            return isOK("SystemRestore", tRet);
        }

        return false;
    }

    /*
     * Remote IOT functions
     */
    public boolean getSecurityCode(short aLanguage, short aSecurityInfoType, String aSecurityInfo) {
        if (isStarted(true) && aSecurityInfo != null) {
            byte tSecurityInfo[] = aSecurityInfo.getBytes();

            return isOK("GetSecurityCode", IOTGetSecurityCode(aLanguage, aSecurityInfoType, tSecurityInfo, IOTDataType.C16(tSecurityInfo.length)));
        }

        return false;
    }

    public boolean verifySecurityCode(short aSecurityInfoType, String aSecurityInfo, int aSecurityCode) {
        if (aSecurityInfo != null) {
            byte tSecurityInfo[] = aSecurityInfo.getBytes();

            return isOK("VerifySecurityCode", IOTVerifySecurityCode(aSecurityInfoType, tSecurityInfo, IOTDataType.C16(tSecurityInfo.length), aSecurityCode));
        }

        return false;
    }

    public boolean userRegister(String aUserName, String aPassword, String aMobile, String aEMail, String aFPQuestion, String aFPAnswer, int aSecurityCode) {
        if (isStarted(true) && aUserName != null && aPassword != null && aMobile != null && aEMail != null && aFPQuestion != null && aFPAnswer != null) {
            byte tUserName[] = aUserName.getBytes();
            byte tPassword[] = aPassword.getBytes();
            byte tMobile[] = aMobile.getBytes();
            byte tEMail[] = aEMail.getBytes();
            byte tFPQuestion[] = aFPQuestion.getBytes();
            byte tFPAnswer[] = aFPAnswer.getBytes();

            return isOK("UserRegister", IOTUserRegister(tUserName, IOTDataType.C16(tUserName.length), tPassword, IOTDataType.C16(tPassword.length),
                    tMobile, IOTDataType.C16(tMobile.length), tEMail, IOTDataType.C16(tEMail.length),
                    tFPQuestion, IOTDataType.C16(tFPQuestion.length), tFPAnswer, IOTDataType.C16(tFPAnswer.length), aSecurityCode));
        }

        return false;
    }

    public long requestAuthID(String aUserName, String aPassword) {
        if (isStarted() && aUserName != null && aPassword != null) {
            byte tUserName[] = aUserName.getBytes();
            byte tPassword[] = aPassword.getBytes();
            long tAuthID[] = new long[1];

            if (isOK("Request AuthID", IOTRequestAuthID(tUserName, IOTDataType.C16(tUserName.length), tPassword, IOTDataType.C16(tPassword.length), tAuthID))) {
                return tAuthID[0];
            }
        }

        return KINVALID_ID;
    }

    public boolean cancelAuthID(long aAuthID) {
        if (isStarted()) {
            return isOK("Cancel AuthID", IOTCancelAuthID(aAuthID));
        }

        return false;
    }

    public boolean login(String aUserName, String aPassword) {
        if (isStarted(true) && aUserName != null && aPassword != null) {
            byte tUserName[] = aUserName.getBytes();
            byte tPassword[] = aPassword.getBytes();

            return isOK("Login", IOTLogin(tUserName, IOTDataType.C16(tUserName.length), tPassword, IOTDataType.C16(tPassword.length)));
        }

        return false;
    }

    public boolean logout() {
        if (isStarted(true)) {
            boolean tOK;

            iWaitingLogout = true;
            tOK = EIOT_RESULT.ISOK(IOTLogout());
            iWaitingLogout = false;

            return tOK;
        } else {
            return false;
        }
    }

    public boolean isUserRecord() {
        return IOTGetLogonUser() != KINVALID_USER_ID;
    }

    public long getLogonUser() {
        return IOTGetLogonUser();
    }

    public String getLogonUserName() {
        byte tUser[] = new byte[KIOT_USER_NAME_MAX_LENGTH];

        if (tUser != null) {
            short tUserLength[] = {IOTDataType.C16(tUser.length)};

            if (EIOT_RESULT.ISOK(IOTGetLogonUserName(tUser, tUserLength))) {
                return (new String(tUser, 0, tUserLength[0])).trim();
            }
        }

        return null;
    }

    public boolean resetPassword(short aLanguage, short aSecurityInfoType, String aUserName, String aSecurityInfo, int aSecurityCode) {
        if (isStarted() && aUserName != null && aSecurityInfo != null) {
            byte tUserName[] = aUserName.getBytes();
            byte tSecurityInfo[] = aSecurityInfo.getBytes();

            return isOK("ResetPassword", IOTResetPassword(aLanguage, aSecurityInfoType, tUserName, IOTDataType.C16(tUserName.length), tSecurityInfo, IOTDataType.C16(tSecurityInfo.length), aSecurityCode));
        }

        return false;
    }

    public String getFPQuestion(String aUserName, String aSecurityInfo) {
        if (isStarted() && aUserName != null && aSecurityInfo != null) {
            byte tUserName[] = aUserName.getBytes();
            byte tSecurityInfo[] = aSecurityInfo.getBytes();
            byte tFPQuestion[] = new byte[KIOT_FP_QUESTION_MAX_LENGTH];
            short tFPQLength[] = {IOTDataType.C16(tFPQuestion.length)};

            if (isOK("GetFPQuestion", IOTGetFPQuestion(tUserName, IOTDataType.C16(tUserName.length), tSecurityInfo, IOTDataType.C16(tSecurityInfo.length), tFPQuestion, tFPQLength))) {
                return (new String(tFPQuestion, 0, tFPQLength[0])).trim();
            }
        }

        return null;
    }

    public boolean changePasswordByOld(String aOldPwd, String aNewPwd) {
        if (isStarted() && aOldPwd != null && aNewPwd != null) {
            byte tOldPwd[] = aOldPwd.getBytes();
            byte tNewPwd[] = aNewPwd.getBytes();

            return isOK("ChangePasswordByOld", IOTChangePasswordByOld(tOldPwd, IOTDataType.C16(tOldPwd.length), tNewPwd, IOTDataType.C16(tNewPwd.length), 0));
        }

        return false;
    }

    public boolean changePasswordByAnswer(String aUserName, String aFPAnswer, String aNewPwd) {
        if (isStarted() && aUserName != null && aFPAnswer != null && aNewPwd != null) {
            byte tUserName[] = aUserName.getBytes();
            byte tFPAnswer[] = aFPAnswer.getBytes();
            byte tNewPwd[] = aNewPwd.getBytes();

            return isOK("ChangePasswordByAnswer", IOTChangePasswordByAnswer(tUserName, IOTDataType.C16(tUserName.length), tFPAnswer, IOTDataType.C16(tFPAnswer.length), tNewPwd, IOTDataType.C16(tNewPwd.length)));
        }

        return false;
    }

    public boolean changeUserInfo(String aUserName, String aPassword, String aMobile, String aEMail, String aFPQuestion, String aFPAnswer, long aReLoginLimit, int aSecurityCode) {
        if (isStarted() && aUserName != null && aPassword != null && aMobile != null && aEMail != null && aFPQuestion != null && aFPAnswer != null) {
            byte tUserName[] = aUserName.getBytes();
            byte tPassword[] = aPassword.getBytes();
            byte tMobile[] = aMobile.getBytes();
            byte tEMail[] = aEMail.getBytes();
            byte tFPQuestion[] = aFPQuestion.getBytes();
            byte tFPAnswer[] = aFPAnswer.getBytes();

            return isOK("ChangeUserInfo", IOTChangeUserInfo(tUserName, IOTDataType.C16(tUserName.length), tPassword, IOTDataType.C16(tPassword.length),
                    tMobile, IOTDataType.C16(tMobile.length), tEMail, IOTDataType.C16(tEMail.length),
                    tFPQuestion, IOTDataType.C16(tFPQuestion.length), tFPAnswer, IOTDataType.C16(tFPAnswer.length), aReLoginLimit, aSecurityCode, 0));
        }

        return false;
    }

    /**
     * @deprecated Use {@link #retriveDeviceInfoList} for better performance
     */
    public boolean retriveDeviceInfo(long aDevID, IOTDeviceInfoEx aDeviceInfo) {
        if (isStarted() && aDeviceInfo != null) {
            short tType[] = {0};
            short tModel[] = {0};
            int tVersion[] = {0};
            int tRegisterSpace[] = {0};

            if (isOK("RetriveDeviceInfo " + aDevID, IOTRetriveDeviceInfo(aDevID, tType, tModel, tVersion, tRegisterSpace))) {
            	aDeviceInfo.iID = aDevID;
                aDeviceInfo.iType = IOTDataType.CU16(tType[0]);
                aDeviceInfo.iModel = IOTDataType.CU16(tModel[0]);
                aDeviceInfo.iVersion = IOTDataType.CU32(tVersion[0]);
                aDeviceInfo.iRegisterSpace = IOTDataType.CU32(tRegisterSpace[0]);

                return true;
            }
        }

        return false;
    }

    public ArrayList<IOTDeviceInfoEx> retriveDeviceInfoList(long aDevList[]) {
        if (isStarted() && aDevList != null && aDevList.length > 0) {
        	int tCount;
        	long tDevList[] = new long[16];
			short tType[] = new short[16];
			short tModel[] = new short[16];
			int tVersion[] = new int[16];
			int tRegisterSpace[] = new int[16];
			int tListSize[] = {16};
			
        	IOTDeviceInfoEx tInfo;
        	ArrayList<IOTDeviceInfoEx> tInfos = new ArrayList<IOTDeviceInfoEx>();
        	
        	for (int i = 0; i < aDevList.length; i+=16){
        		tCount = aDevList.length - i;
        		
        		if (tCount == 1){
        			tInfo = new IOTDeviceInfoEx();
        			
        			if (retriveDeviceInfo(aDevList[i], tInfo)){
        				tInfos.add(tInfo);
        			}
        		}else{
					if (tCount > 16){
        				tCount = 16;
        			}
        			
					tListSize[0] = tCount;
					System.arraycopy(aDevList, i, tDevList, 0, tCount);
					
		            if (isOK("RetriveDeviceInfoList", IOTRetriveDeviceInfoList(tDevList, tType, tModel, tVersion, tRegisterSpace, tListSize))) {
		            	for (int j = 0; j < tListSize[0]; j++){
		            		tInfo = new IOTDeviceInfoEx(tDevList[j], tType[j], tModel[j], tVersion[j], tRegisterSpace[j]);
		            		
		            		tInfos.add(tInfo);
		            	}
		            }
        		}
        	}
        	
        	if (tInfos.size() > 0){
        		return tInfos;
        	}
        }

        return null;
    }

    public long retriveUserID(String aUserName) {
    	if (aUserName != null){
	        byte tUser[] = aUserName.getBytes();
	
	        if (tUser != null && tUser.length > 0) {
	        	long tID[] = {9};
	        	
	            if (EIOT_RESULT.ISOK(IOTRetriveUserID(tUser, (short) tUser.length, tID))) {
	                return tID[0];
	            }
	        }
    	}

        return IOTNetAPIConst.KINVALID_USER_ID;
    }

    public String retriveUserName(long aUserID) {
        byte tUser[] = new byte[KIOT_USER_NAME_MAX_LENGTH];

        if (isStarted() && tUser != null) {
            short tUserLength[] = {IOTDataType.C16(tUser.length)};

            if (isOK("RetriveUserName", IOTRetriveUserName(aUserID, tUser, tUserLength))) {
                return (new String(tUser, 0, tUserLength[0])).trim();
            }
        }

        return null;
    }

    public boolean retriveUserInfo(String aPassword, IOTString aMobile, IOTString aEMail, IOTString aFPQuestion, IOTString aFPAnswer, IOTLong aReLoginLimit) {
        if (isStarted() && aPassword != null) {
            byte tPassword[] = aPassword.getBytes();
            byte tMobile[] = new byte[KIOT_MOBILE_MAX_LENGTH];
            short tMobileLength[] = {IOTDataType.C16(tMobile.length)};
            byte tEMail[] = new byte[KIOT_EMAIL_MAX_LENGTH];
            short tEMailLength[] = {IOTDataType.C16(tEMail.length)};
            byte tFPQuestion[] = new byte[KIOT_FP_QUESTION_MAX_LENGTH];
            short tFPQLength[] = {IOTDataType.C16(tFPQuestion.length)};
            byte tFPAnswer[] = new byte[KIOT_FP_ANSWER_MAX_LENGTH];
            short tFPALength[] = {IOTDataType.C16(tFPAnswer.length)};
            long tReLoginLimit[] = {0};

            if (isOK("RetriveUserInfo", IOTRetriveUserInfo(tPassword, IOTDataType.C16(tPassword.length),
                    tMobile, tMobileLength, tEMail, tEMailLength,
                    tFPQuestion, tFPQLength, tFPAnswer, tFPALength, tReLoginLimit, 0))) {
                if (aMobile != null) {
                    aMobile.setValue(tMobile, 0, tMobileLength[0]);
                }
                if (aEMail != null) {
                    aEMail.setValue(tEMail, 0, tEMailLength[0]);
                }
                if (aFPQuestion != null) {
                    aFPQuestion.setValue(tFPQuestion, 0, tFPQLength[0]);
                }
                if (aFPAnswer != null) {
                    aFPAnswer.setValue(tFPAnswer, 0, tFPALength[0]);
                }
                if (aReLoginLimit != null) {
                    aReLoginLimit.setValue(tReLoginLimit[0]);
                }

                return true;
            }
        }

        return false;
    }

    /**
     * @deprecated Use {@link #getOnlineDeviceList} for better performance
     */
    public int getOnlineDeviceFirst(IOTLong aID, IOTLong aDevID) {
        if (isStarted() && aID != null && aDevID != null) {
            int tRet;
            long tID[] = {0};
            long tDevID[] = {0};

            if (isOK("GetOnlineDeviceFirst", tRet = IOTGetOnlineDeviceFirst(tID, tDevID, 0))) {
                aID.setValue(tID[0]);
                aDevID.setValue(tDevID[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    /**
     * @deprecated Use {@link #getOnlineDeviceList} for better performance
     */
    public int getOnlineDeviceNext(IOTLong aID, IOTLong aDevID) {
        if (isStarted() && aID != null && aDevID != null) {
            int tRet;
            long tID[] = {aID.getValue()};
            long tDevID[] = {0};

            if (isOK("GetOnlineDeviceNext", tRet = IOTGetOnlineDeviceNext(tID, tDevID, 0))) {
                aID.setValue(tID[0]);
                aDevID.setValue(tDevID[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public ArrayList<Long> getOnlineDeviceList() {
        if (isStarted()) {
            int tRet;
            long tID[] = {0};
            long tDeviceList[] = new long[16];
            short tCount[] = {0};
            short tRemain[] = {0};
            ArrayList<Long> tList = new ArrayList<Long>();

            do {
                tCount[0] = (short) tDeviceList.length;
                tRemain[0] = 0;

                if (tList.size() == 0) {
                    tRet = IOTGetOnlineDeviceListFirst(tID, tDeviceList, tCount, tRemain, 0);
                } else {
                    tRet = IOTGetOnlineDeviceListNext(tID, tDeviceList, tCount, tRemain, 0);
                }

                if (EIOT_RESULT.ISOK(tRet) && tCount[0] > 0) {
                    for (int i = 0; i < tCount[0]; i++) {
                        tList.add(tDeviceList[i]);
                    }
                }
            } while (isOK("GetOnlineDeviceList", tRet) && tRemain[0] > 0);

            if (tList.size() > 0) {
                return tList;
            }
        }

        return null;
    }

    /**
     * @deprecated Use {@link #getDeviceInfoVersionList} for better performance
     */
    public int getDeviceInfoVersion(long aDevID) {
        if (isStarted()) {
            int tVersion[] = {0};

            if (isOK("GetDeviceInfoVersion", IOTGetDeviceInfoVersion(aDevID, tVersion, 0))) {
                return tVersion[0];
            }
        }

        return 0;
    }

    @SuppressWarnings("deprecation")
	public int getDeviceInfoVersionList(long aDevList[], int aVersionList[]) {
    	int tRet = EIOT_RESULT.EIOT_RESULT_UNKNOWN;
    	
        if (isStarted() && aDevList != null && aVersionList != null && aDevList.length > 0 && aVersionList.length >= aDevList.length) {
        	if (aDevList.length == 1){
        		tRet = IOTGetDeviceInfoVersion(aDevList[0], aVersionList, 0);
        	}else{
	        	int tSize[] = {aDevList.length};
	
	            tRet = IOTGetDeviceInfoVersionList(tSize, aDevList, aVersionList, 0);
        	}
        	
        	isOK("GetDeviceInfoVersion", tRet);
        }

        return tRet;
    }

    public long getDeviceInfoCount(long aDevID) {
        if (isStarted()) {
            long tCount[] = {0};

            if (isOK("GetDeviceInfoCount", IOTGetDeviceInfoCount(aDevID, tCount, 0))) {
                return tCount[0];
            }
        }

        return 0;
    }

    public boolean addDeviceInfo(long aDevID, String aName, String aValue) {
        if (isStarted() && aName != null && aValue != null) {
            byte tName[] = aName.getBytes();
            byte tValue[] = aValue.getBytes();

            return isOK("AddDeviceInfo", IOTAddDeviceInfo(aDevID, tName, IOTDataType.C16(tName.length), tValue, IOTDataType.C16(tValue.length), 0));
        }

        return false;
    }

    public boolean editDeviceInfo(long aDevID, String aName, String aValue) {
        if (isStarted() && aName != null && aValue != null) {
            byte tName[] = aName.getBytes();
            byte tValue[] = aValue.getBytes();

            return isOK("EditDeviceInfo", IOTEditDeviceInfo(aDevID, tName, IOTDataType.C16(tName.length), tValue, IOTDataType.C16(tValue.length), 0));
        }

        return false;
    }

    public boolean removeDeviceInfo(long aDevID, String aName) {
        if (isStarted() && aName != null) {
            byte tName[] = aName.getBytes();

            return isOK("RemoveDeviceInfo", IOTRemoveDeviceInfo(aDevID, tName, IOTDataType.C16(tName.length), 0));
        }

        return false;
    }

    public String queryDeviceInfo(long aDevID, String aName) {
        if (isStarted() && aName != null) {
            byte tName[] = aName.getBytes();
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH];
            short tValueLength[] = {KIOT_INFO_VALUE_MAX_LENGTH};

            if (isOK("QueryDeviceInfo", IOTQueryDeviceInfo(aDevID, tName, IOTDataType.C16(tName.length), tValue, tValueLength, 0))) {
                return (new String(tValue, 0, tValueLength[0])).trim();
            }
        }

        return null;
    }

    private HashMap<Long, Boolean> iUpdatedDeviceInfoList = null;

    @SuppressLint("UseSparseArrays")
    public boolean updateDeviceInfoList(long aDevID) {
        if (isStarted()) {
            if (iCache != null && iCache.length > 0) {
                int tRet = IOTUpdateDeviceInfoList(aDevID, 0);

                if (iUpdatedDeviceInfoList == null) {
                    iUpdatedDeviceInfoList = new HashMap<Long, Boolean>();
                } else if (iUpdatedDeviceInfoList.containsKey(aDevID)) {
                    Boolean tState = iUpdatedDeviceInfoList.get(aDevID);

                    if (tState != null && tState == isRemoteReady()) {
                        return isOK("UpdateDeviceInfoList", tRet);
                    }
                }

                iUpdatedDeviceInfoList.put(aDevID, isRemoteReady());

                return (tRet == EIOT_RESULT.EIOT_RESULT_NO_EFFECT) || isOK("UpdateDeviceInfoList", tRet);
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    /**
     * @deprecated Use {@link #getDeviceInfoList} for better performance
     */
    public int getDeviceInfoFirst(long aDevID, IOTInfoWithValue aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {0};
            byte tName[] = new byte[KIOT_INFO_NAME_MAX_LENGTH];
            short tNameLength[] = {IOTDataType.C16(tName.length)};
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH];
            short tValueLength[] = {IOTDataType.C16(tValue.length)};

            if (isOK("GetDeviceInfoFirst", tRet = IOTGetDeviceInfoFirst(aDevID, tID, tName, tNameLength, tValue, tValueLength, 0))) {
                aInfo.setID(tID[0]);
                aInfo.setName(tName, 0, tNameLength[0]);
                aInfo.setValue(tValue, 0, tValueLength[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    /**
     * @deprecated Use {@link #getDeviceInfoList} for better performance
     */
    public int getDeviceInfoNext(long aDevID, IOTInfoWithValue aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {aInfo.getID()};
            byte tName[] = new byte[KIOT_INFO_NAME_MAX_LENGTH];
            short tNameLength[] = {IOTDataType.C16(tName.length)};
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH];
            short tValueLength[] = {IOTDataType.C16(tValue.length)};

            if (isOK("GetDeviceInfoNext", tRet = IOTGetDeviceInfoNext(aDevID, tID, tName, tNameLength, tValue, tValueLength, 0))) {
                aInfo.setID(tID[0]);
                aInfo.setName(tName, 0, tNameLength[0]);
                aInfo.setValue(tValue, 0, tValueLength[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public ArrayList<IOTInfoWithValue> getDeviceInfoList(long aDevID) {
        if (isStarted()) {
            int tRet;
            long tID[] = {0};
            byte tName[] = new byte[KIOT_INFO_NAME_MAX_LENGTH << 2];
            short tNameLen[] = new short[2 << 2];
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH << 2];
            short tValueLen[] = new short[2 << 2];
        	int tListSize[] = {0};
        	int tRemain[] = {0};
        	ArrayList<IOTInfoWithValue> tInfos = null;
        	
        	do{
        		tListSize[0] = 4;
        		tRemain[0] = 0;
        		
        		if (tInfos == null){
        			tRet = IOTGetDeviceInfoListFirst(aDevID, tID, tName, tNameLen, tValue, tValueLen, tListSize, tRemain, 0);
        		}else{
        			tRet = IOTGetDeviceInfoListNext(aDevID, tID, tName, tNameLen, tValue, tValueLen, tListSize, tRemain, 0);
        		}
        		
        		if (isOK(tInfos == null ? "GetDeviceInfoListFirst" : "GetDeviceInfoListNext", tRet)){
        			if (tListSize[0] > 0){
        				if (tInfos == null){
        					tInfos = new ArrayList<IOTInfoWithValue>();
        				}
        				
        				for (int i = 0; i < tListSize[0]; i++){
        					tInfos.add(new IOTInfoWithValue(tID[0], new String(tName, i * KIOT_INFO_NAME_MAX_LENGTH, tNameLen[i]), new String(tValue, i * KIOT_INFO_VALUE_MAX_LENGTH, tValueLen[i])));
        				}
        			}else{
        				break;
        			}
        		}
        	}while(ISTRUE(tRemain[0]));

        	if (EIOT_RESULT.ISOK(tRet) || tRet == EIOT_RESULT.EIOT_RESULT_GENERAL_NO_DEVICE_INFO){
        		return tInfos;
        	}
        }

        return null;
    }

    public int getUserInfoVersion() {
        if (isStarted()) {
            int tVersion[] = {0};

            if (isOK("GetUserInfoVersion", IOTGetUserInfoVersion(tVersion, 0))) {
                return tVersion[0];
            }
        }

        return 0;
    }

    public long getUserInfoCount() {
        if (isStarted()) {
            long tCount[] = {0};

            if (isOK("GetUserInfoCount", IOTGetUserInfoCount(tCount, 0))) {
                return tCount[0];
            }
        }

        return 0;
    }

    public boolean addPublicUserInfo(String aName, String aValue) {
        if (isStarted() && aName != null && aValue != null) {
            byte tName[] = aName.getBytes();
            byte tValue[] = aValue.getBytes();

            return isOK("AddPublicUserInfo", IOTAddPublicUserInfo(tName, IOTDataType.C16(tName.length), tValue, IOTDataType.C16(tValue.length), 0));
        }

        return false;
    }

    public boolean editPublicUserInfo(String aName, String aValue) {
        if (isStarted() && aName != null && aValue != null) {
            byte tName[] = aName.getBytes();
            byte tValue[] = aValue.getBytes();

            return isOK("EditPublicUserInfo", IOTEditPublicUserInfo(tName, IOTDataType.C16(tName.length), tValue, IOTDataType.C16(tValue.length), 0));
        }

        return false;
    }

    public boolean removePublicUserInfo(String aName) {
        if (isStarted() && aName != null) {
            byte tName[] = aName.getBytes();

            return isOK("RemovePublicUserInfo", IOTRemovePublicUserInfo(tName, IOTDataType.C16(tName.length), 0));
        }

        return false;
    }

    public String queryPublicUserInfo(String aName) {
        if (isStarted() && aName != null) {
            byte tName[] = aName.getBytes();
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH];
            short tValueLength[] = {KIOT_INFO_VALUE_MAX_LENGTH};

            if (isOK("QueryPublicUserInfo", IOTQueryPublicUserInfo(tName, IOTDataType.C16(tName.length), tValue, tValueLength, 0))) {
                return (new String(tValue, 0, tValueLength[0])).trim();
            }
        }

        return null;
    }

    private boolean iUpdatedPublicUserInfoRemoteState = false;
    private long iUpdatedPublicUserInfoListUser = KINVALID_USER_ID;

    public boolean updatePublicUserInfoList() {
        if (isStarted()) {
            if (iCache != null && iCache.length > 0) {
                int tRet = IOTUpdatePublicUserInfoList(0);

                if (iUpdatedPublicUserInfoRemoteState != isRemoteReady() || iUpdatedPublicUserInfoListUser != getLogonUser()) {
                    iUpdatedPublicUserInfoRemoteState = isRemoteReady();
                    iUpdatedPublicUserInfoListUser = getLogonUser();

                    return (tRet == EIOT_RESULT.EIOT_RESULT_NO_EFFECT) || isOK("UpdatePublicUserInfoList", tRet);
                } else {
                    return isOK("UpdatePublicUserInfoList", tRet);
                }
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    /**
     * @deprecated Use {@link #getPublicUserInfoList} for better performance
     */
    public int getPublicUserInfoFirst(IOTInfoWithValue aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {0};
            byte tName[] = new byte[KIOT_INFO_NAME_MAX_LENGTH];
            short tNameLength[] = {IOTDataType.C16(tName.length)};
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH];
            short tValueLength[] = {IOTDataType.C16(tValue.length)};

            if (isOK("GetPublicUserInfoFirst", tRet = IOTGetPublicUserInfoFirst(tID, tName, tNameLength, tValue, tValueLength, 0))) {
                aInfo.setID(tID[0]);
                aInfo.setName(tName, 0, tNameLength[0]);
                aInfo.setValue(tValue, 0, tValueLength[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    /**
     * @deprecated Use {@link #getPublicUserInfoList} for better performance
     */
    public int getPublicUserInfoNext(IOTInfoWithValue aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {aInfo.getID()};
            byte tName[] = new byte[KIOT_INFO_NAME_MAX_LENGTH];
            short tNameLength[] = {IOTDataType.C16(tName.length)};
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH];
            short tValueLength[] = {IOTDataType.C16(tValue.length)};

            if (isOK("GetPublicUserInfoNext", tRet = IOTGetPublicUserInfoNext(tID, tName, tNameLength, tValue, tValueLength, 0))) {
                aInfo.setID(tID[0]);
                aInfo.setName(tName, 0, tNameLength[0]);
                aInfo.setValue(tValue, 0, tValueLength[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }
    
    public ArrayList<IOTInfoWithValue> getPublicUserInfoList() {
        if (isStarted()) {
            int tRet;
            long tID[] = {0};
            byte tName[] = new byte[KIOT_INFO_NAME_MAX_LENGTH << 2];
            short tNameLen[] = new short[2 << 2];
            byte tValue[] = new byte[KIOT_INFO_VALUE_MAX_LENGTH << 2];
            short tValueLen[] = new short[2 << 2];
        	int tListSize[] = {0};
        	int tRemain[] = {0};
        	ArrayList<IOTInfoWithValue> tInfos = null;
        	
        	do{
        		tListSize[0] = 4;
        		tRemain[0] = 0;
        		
        		if (tInfos == null){
        			tRet = IOTGetPublicUserInfoListFirst(tID, tName, tNameLen, tValue, tValueLen, tListSize, tRemain, 0);
        		}else{
        			tRet = IOTGetPublicUserInfoListNext(tID, tName, tNameLen, tValue, tValueLen, tListSize, tRemain, 0);
        		}
        		
        		if (isOK(tInfos == null ? "GetPublicUserInfoListFirst" : "GetPublicUserInfoListNext", tRet)){
        			if (tListSize[0] > 0){
        				if (tInfos == null){
        					tInfos = new ArrayList<IOTInfoWithValue>();
        				}
        				
        				for (int i = 0; i < tListSize[0]; i++){
        					tInfos.add(new IOTInfoWithValue(tID[0], new String(tName, i * KIOT_INFO_NAME_MAX_LENGTH, tNameLen[i]), new String(tValue, i * KIOT_INFO_VALUE_MAX_LENGTH, tValueLen[i])));
        				}
        			}else{
        				break;
        			}
        		}
        	}while(ISTRUE(tRemain[0]));

        	if (EIOT_RESULT.ISOK(tRet) || tRet == EIOT_RESULT.EIOT_RESULT_GENERAL_NO_USER_INFO){
        		return tInfos;
        	}
        }

        return null;
    }
    
    public boolean authRequest(long aDevID, boolean aRequestQuit) {
        if (isStarted()) {
            int tResult = IOTAuthRequest(aDevID, aRequestQuit ? TRUE : FALSE, 0);

            if (EIOT_RESULT.ISOK(tResult) || tResult == EIOT_RESULT.EIOT_RESULT_GENERAL_PERMISSION_WAITTING) {
                return true;
            } else {
                return isOK("AuthRequest", tResult);
            }
        } else {
            return false;
        }
    }

    public boolean authUpdate(int aAuthState, long aUser, long aDevID, long aTimeout) {
        if (isStarted()) {
            return isOK("AuthUpdate", IOTAuthUpdate(aAuthState, aUser, aDevID, aTimeout, 0));
        } else {
            return false;
        }
    }

    public int getAuthDeviceVersion() {
        if (isStarted()) {
            int tVersion[] = {0};

            if (isOK("GetAuthDeviceVersion", IOTGetAuthDeviceVersion(tVersion, 0))) {
                return tVersion[0];
            }
        }

        return 0;
    }

    public int getAuthDeviceCount() {
        if (isStarted()) {
            int tCount[] = {0};

            if (isOK("GetAuthDeviceCount", IOTGetAuthDeviceCount(tCount, 0))) {
                return tCount[0];
            }
        }

        return 0;
    }

    /**
     * @deprecated Use {@link #getAuthDeviceList} for better performance
     */
    public int getAuthDeviceFirst(IOTAuthInfo aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {0};
            long tDevID[] = {0};
            int tAuthState[] = {0};

            if (isOK("GetAuthDeviceFirst", tRet = IOTGetAuthDeviceFirst(tID, tDevID, tAuthState, 0))) {
                aInfo.iID = tID[0];
                aInfo.iAuthObject = tDevID[0];
                aInfo.iAuthState = tAuthState[0];
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    /**
     * @deprecated Use {@link #getAuthDeviceList} for better performance
     */
    public int getAuthDeviceNext(IOTAuthInfo aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {aInfo.iID};
            long tDevID[] = {0};
            int tAuthState[] = {0};

            if (isOK("GetAuthDeviceNext", tRet = IOTGetAuthDeviceNext(tID, tDevID, tAuthState, 0))) {
                aInfo.iID = tID[0];
                aInfo.iAuthObject = tDevID[0];
                aInfo.iAuthState = tAuthState[0];
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public ArrayList<IOTAuthInfo> getAuthDeviceList(IOTULong aIgnoreVersion) {
        if (isStarted()) {
            int tRet;
            long tID[] = {0};
            long tDeviceList[] = new long[16];
            byte tAuthList[] = new byte[16];
            short tCount[] = {0};
            short tRemain[] = {0};
            ArrayList<IOTAuthInfo> tList = new ArrayList<IOTAuthInfo>();

            if (aIgnoreVersion != null) {
                long tVersion = getAuthDeviceVersion();

                if (aIgnoreVersion.getValue() != 0) {
                    if (tVersion == aIgnoreVersion.getValue()) {
                        return null;
                    } else {
                        aIgnoreVersion.setValue(tVersion);
                    }
                } else {
                    aIgnoreVersion.setValue(tVersion);
                }
            }

            do {
                tCount[0] = (short) tDeviceList.length;
                tRemain[0] = 0;

                if (tList.size() == 0) {
                    tRet = IOTGetAuthDeviceListFirst(tID, tDeviceList, tAuthList, tCount, tRemain, 0);
                } else {
                    tRet = IOTGetAuthDeviceListNext(tID, tDeviceList, tAuthList, tCount, tRemain, 0);
                }

                if (EIOT_RESULT.ISOK(tRet) && tCount[0] > 0) {
                    for (int i = 0; i < tCount[0]; i++) {
                        tList.add(new IOTAuthInfo(0, tDeviceList[i], tAuthList[i]));
                    }
                }
            } while (isOK("GetAuthDeviceList", tRet) && tRemain[0] > 0);

            if (tList.size() > 0) {
                return tList;
            }
        }

        return null;
    }

    public ArrayList<IOTAuthInfo> getAuthDeviceList() {
        return getAuthDeviceList(null);
    }

    public int getAuthUserVersion(long aDevID) {
        if (isStarted()) {
            int tVersion[] = {0};

            if (isOK("GetAuthUserVersion", IOTGetAuthUserVersion(aDevID, tVersion, 0))) {
                return tVersion[0];
            }
        }

        return 0;
    }

    public int getAuthUserCount(long aDevID) {
        if (isStarted()) {
            int tCount[] = {0};

            if (isOK("GetAuthUserCount", IOTGetAuthUserCount(aDevID, tCount, 0))) {
                return tCount[0];
            }
        }

        return 0;
    }

    public int getAuthUserFirst(long aDevID, IOTAuthInfo aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {0};
            long tUserID[] = {0};
            int tAuthState[] = {0};

            if (isOK("GetAuthUserFirst", tRet = IOTGetAuthUserFirst(aDevID, tID, tUserID, tAuthState, 0))) {
                aInfo.iID = tID[0];
                aInfo.iAuthObject = tUserID[0];
                aInfo.iAuthState = tAuthState[0];
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public int getAuthUserNext(long aDevID, IOTAuthInfo aInfo) {
        if (isStarted() && aInfo != null) {
            int tRet;
            long tID[] = {aInfo.iID};
            long tUserID[] = {0};
            int tAuthState[] = {0};

            if (isOK("GetAuthUserNext", tRet = IOTGetAuthUserNext(aDevID, tID, tUserID, tAuthState, 0))) {
                aInfo.iID = tID[0];
                aInfo.iAuthObject = tUserID[0];
                aInfo.iAuthState = tAuthState[0];
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public IOTRegLog readRegChangeLogPrev(long aDevID, long aRefID, short aAddress, short aSize) {
        if (isStarted()) {
            if(aSize <= 0){
                aSize = KIOT_DEVICE_REGISTER_PAGE_SIZE;
            }
            long tLogID[] = {0};
            long tLogTime[] = {0};
            short tAddress[] = {aAddress};
            short tSize[] = {aSize};
            byte tData[] = new byte[aSize];

            if (isOK("ReadRegChangeLogPrev", IOTReadRegChangeLogPrev(aDevID, aRefID, tLogID, tLogTime, tAddress, tSize, tData, 0))) {
                return new IOTRegLog(tLogID[0], SplitTime(tLogTime[0]), aDevID, tAddress[0], tSize[0], tData);
            }
        }
        return null;
    }

    public long getBatchVersion(long aDevID) {
        if (isStarted()) {
            long tVersion[] = {0};

            if (isOK("GetBatchVersion", IOTGetBatchVersion(aDevID, tVersion, 0))) {
                return tVersion[0];
            }
        }

        return 0;
    }

    public boolean addBatch(String aName, IOTLong aID) {
        if (isStarted() && aName != null) {
            byte tName[] = aName.getBytes();
            long tID[] = {0};

            if (isOK("AddBatch", IOTAddBatch(tName, IOTDataType.C16(tName.length), tID, 0))) {
                if (aID != null) {
                    aID.setValue(tID[0]);
                }

                return true;
            }
        }

        return false;
    }

    public boolean editBatch(long aBatchID, String aName) {
        if (isStarted() && aName != null) {
            byte tName[] = aName.getBytes();

            return isOK("EditBatch", IOTEditBatch(aBatchID, tName, IOTDataType.C16(tName.length), 0));
        }

        return false;
    }

    public boolean removeBatch(long aBatchID) {
        if (isStarted()) {
            int tRet = IOTRemoveBatch(aBatchID, 0);

            if (tRet == EIOT_RESULT.EIOT_RESULT_GENERAL_NO_BATCH) {
                return true;
            } else {
                return isOK("RemoveBatch", tRet);
            }
        } else {
            return false;
        }
    }

    public int getBatchFirst(IOTBatchInfo aBatchInfo) {
        if (isStarted() && aBatchInfo != null) {
            int tRet;
            long tID[] = {0};
            byte tName[] = new byte[KIOT_BATCH_NAME_MAX_LENGTH];
            short tNameLength[] = {IOTDataType.C16(tName.length)};

            if (isOK("GetBatchFirst", tRet = IOTGetBatchFirst(tID, tName, tNameLength, 0))) {
                aBatchInfo.setID(tID[0]);
                aBatchInfo.setName(tName, 0, tNameLength[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public int getBatchNext(IOTBatchInfo aBatchInfo) {
        if (isStarted() && aBatchInfo != null) {
            int tRet;
            long tID[] = {aBatchInfo.getID()};
            byte tName[] = new byte[KIOT_BATCH_NAME_MAX_LENGTH];
            short tNameLength[] = {IOTDataType.C16(tName.length)};

            if (isOK("GetBatchNext", tRet = IOTGetBatchNext(tID, tName, tNameLength, 0))) {
                aBatchInfo.setID(tID[0]);
                aBatchInfo.setName(tName, 0, tNameLength[0]);
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public boolean addBatchItem(long aBatchID, IOTBatchItemInfo aItemInfo) {
        if (isStarted() && aItemInfo != null) {
            long tItemID[] = {0};

            if (isOK("AddBatchItem", IOTAddBatchItem(aBatchID, aItemInfo.iDevID, IOTDataType.C16(aItemInfo.iAddress), IOTDataType.C16(aItemInfo.iSize), aItemInfo.iData, tItemID, 0))) {
                aItemInfo.iID = tItemID[0];

                return true;
            }
        }

        return false;
    }

    public boolean editBatchItem(long aBatchID, IOTBatchItemInfo aItemInfo) {
        if (isStarted() && aItemInfo != null) {
            int tRet = IOTEditBatchItem(aBatchID, aItemInfo.iID, aItemInfo.iDevID, IOTDataType.C16(aItemInfo.iAddress), IOTDataType.C16(aItemInfo.iSize), aItemInfo.iData, 0);

            if (tRet == EIOT_RESULT.EIOT_RESULT_NO_EFFECT) {
                return true;
            } else {
                return isOK("EditBatchItem", tRet);
            }
        }

        return false;
    }

    public boolean removeBatchItem(long aBatchID, long aItemID) {
        if (isStarted()) {
            int tRet = IOTRemoveBatchItem(aBatchID, aItemID, 0);

            if (tRet == EIOT_RESULT.EIOT_RESULT_GENERAL_NO_BATCH || tRet == EIOT_RESULT.EIOT_RESULT_GENERAL_BATCH_NO_ITEM) {
                return true;
            } else {
                return isOK("RemoveBatchItem", tRet);
            }
        } else {
            return false;
        }
    }

    public int getBatchItemFirst(long aBatchID, IOTBatchItemInfo aItemInfo) {
        if (isStarted() && aItemInfo != null) {
            int tRet;
            long tItemID[] = {0};
            long tDevID[] = {0};
            short tAddress[] = {0};
            byte tData[] = new byte[KIOT_BATCH_ITEM_DATA_MAX_LENGTH];
            short tSize[] = {IOTDataType.C16(tData.length)};

            if (isOK("GetBatchItemFirst", tRet = IOTGetBatchItemFirst(aBatchID, tItemID, tDevID, tAddress, tSize, tData, 0))) {
                aItemInfo.iID = tItemID[0];
                aItemInfo.iDevID = tDevID[0];
                aItemInfo.iAddress = IOTDataType.CU16(tAddress[0]);
                aItemInfo.setData(tData, IOTDataType.CU16(tSize[0]));
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public int getBatchItemNext(long aBatchID, IOTBatchItemInfo aItemInfo) {
        if (isStarted() && aItemInfo != null) {
            int tRet;
            long tItemID[] = {aItemInfo.iID};
            long tDevID[] = {0};
            short tAddress[] = {0};
            byte tData[] = new byte[KIOT_BATCH_ITEM_DATA_MAX_LENGTH];
            short tSize[] = {IOTDataType.C16(tData.length)};

            if (isOK("GetBatchItemNext", tRet = IOTGetBatchItemNext(aBatchID, tItemID, tDevID, tAddress, tSize, tData, 0))) {
                aItemInfo.iID = tItemID[0];
                aItemInfo.iDevID = tDevID[0];
                aItemInfo.iAddress = IOTDataType.CU16(tAddress[0]);
                aItemInfo.setData(tData, IOTDataType.CU16(tSize[0]));
            }

            return tRet;
        }

        return EIOT_RESULT.EIOT_RESULT_GENERAL_BAD_STATUS;
    }

    public boolean executeBatch(long aBatchID) {
        boolean tLocked = false;
        boolean tRet;

        if (!isStarted(true)) {
            tLocked = true;

            threadLock();

            if (!startOneshot()) {
                threadUnlock();

                return false;
            }
        }

        tRet = isOK("ExecuteBatch", IOTExecuteBatch(aBatchID, 0));

        if (tLocked) {
            threadUnlock();
        }

        return tRet;
    }

    public boolean previewBatch(long aBatchID) {
        boolean tLocked = false;
        boolean tRet;

        if (!isStarted(true)) {
            tLocked = true;

            threadLock();

            if (!startOneshot()) {
                threadUnlock();

                return false;
            }
        }

        tRet = isOK("PreviewBatch", IOTPreviewBatch(aBatchID, 0));

        if (tLocked) {
            threadUnlock();
        }

        return tRet;
    }

    public boolean upgradeScan(short aType, short aModel, int aVersion, IOTCallback.UpgradeDeviceInfoCallback aCallback) {
        if (isStarted()) {
            return isOK("UpgradeScan", IOTUpgradeScan(aType, aModel, aVersion, aCallback));
        } else {
            return false;
        }
    }

    /**
     * @deprecated Use {@link #upgradeAllByBinary} for better performance
     */
    public boolean upgradeAll(String aFile, short aType, short aModel, int aVersion, IOTCallback.UpgradeProgressCallback aCallback) {
        if (isStarted() && aFile != null) {
            byte tFile[] = aFile.getBytes();
            byte tFinalFile[] = new byte[tFile.length + 1];

            System.arraycopy(tFile, 0, tFinalFile, 0, tFile.length);
            tFinalFile[tFile.length] = 0;

            return isOK("UpgradeAll", IOTUpgradeAll(tFinalFile, aType, aModel, aVersion, aCallback));
        }

        return false;
    }

    public boolean upgradeAllByBinary(byte aBinary[], int aBinarySize, short aType, short aModel, int aVersion, IOTCallback.UpgradeProgressCallback aCallback) {
        if (isStarted() && aBinary != null && aBinarySize > 0) {
            return isOK("IOTUpgradeAllByBinary", IOTUpgradeAllByBinary(aBinary, aBinarySize, aType, aModel, aVersion, aCallback));
        }

        return false;
    }

    /**
     * @deprecated Use {@link #upgradeOneByBinary} for better performance
     */
    public boolean upgradeOne(String aFile, short aType, short aModel, int aVersion, int aDeviceIP, short aDevicePort, IOTCallback.UpgradeProgressCallback aCallback) {
        if (isStarted() && aFile != null) {
            byte tFile[] = aFile.getBytes();
            byte tFinalFile[] = new byte[tFile.length + 1];

            System.arraycopy(tFile, 0, tFinalFile, 0, tFile.length);
            tFinalFile[tFile.length] = 0;

            return isOK("UpgradeOne", IOTUpgradeOne(tFinalFile, aType, aModel, aVersion, aDeviceIP, aDevicePort, aCallback));
        }

        return false;
    }

    public boolean upgradeOneByBinary(byte aBinary[], int aBinarySize, short aType, short aModel, int aVersion, int aDeviceIP, short aDevicePort, IOTCallback.UpgradeProgressCallback aCallback) {
        if (isStarted() && aBinary != null && aBinarySize > 0) {
            return isOK("UpgradeOneByBinary", IOTUpgradeOneByBinary(aBinary, aBinarySize, aType, aModel, aVersion, aDeviceIP, aDevicePort, aCallback));
        }

        return false;
    }

    public boolean patchVerify(long aDevID, int aUUID, int aVersion) {
        if (isStarted()) {
            return isOK("IOTPatchVerify", IOTPatchVerify(aDevID, aUUID, aVersion));
        }

        return false;
    }

    /**
     * @deprecated Use {@link #installPatchByBinary} for better performance
     */
    public boolean installPatch(String aFile, long aDevID, int aUUID, int aVersion, IOTCallback.UpgradeProgressCallback aCallBack) {
        if (isStarted() && aFile != null) {
            byte tFile[] = aFile.getBytes();
            byte tFinalFile[] = new byte[tFile.length + 1];

            System.arraycopy(tFile, 0, tFinalFile, 0, tFile.length);
            tFinalFile[tFile.length] = 0;

            return isOK("installPatch", IOTInstallPatch(tFinalFile, aDevID, aUUID, aVersion, aCallBack));
        }
        
        return false;
    }

    public boolean installPatchByBinary(byte aBinary[], int aBinarySize, long aDevID, int aUUID, int aVersion, IOTCallback.UpgradeProgressCallback aCallBack) {
        if (isStarted() && aBinary != null && aBinarySize > 0) {
              return isOK("installPatchByBinary", IOTInstallPatchByBinary(aBinary, aBinarySize, aDevID, aUUID, aVersion, aCallBack));
        }
        
        return false;
    }

    /*
      * Extend API functions
      */
    public static String GetStringID(long aID) {
        if (aID >= 0 && aID <= 9) {
            return String.valueOf(aID);
        } else {
            byte tID[] = new byte[16];
            short tIDSize[] = {(short) tID.length};

            if (EIOT_RESULT.ISOK(IOTGetStringID(aID, tID, tIDSize))) {
                return (new String(tID, 0, tIDSize[0])).trim();
            } else {
                return null;
            }
        }
    }

    public static long GetLongID(String aID) {
        if (aID != null) {
            byte tID[] = aID.getBytes();

            if (tID.length == 1 && tID[0] >= '0' && tID[0] <= '9') {
                return tID[0] - '0';
            } else {
                return IOTGetLongID(tID, (short) tID.length);
            }
        }

        return 0;
    }

    public static IOTTime SplitTime(long aTime) {
        int tYear[] = {0};
        byte tMonth[] = {0};
        byte tDay[] = {0};
        byte tHour[] = {0};
        byte tMinute[] = {0};
        byte tSecond[] = {0};

        IOTSplitTime(aTime, tYear, tMonth, tDay, tHour, tMinute, tSecond);

        return new IOTTime(tYear[0], tMonth[0], tDay[0], tHour[0], tMinute[0], tSecond[0]);
    }
    
    public static short GetLanguageIntID(){
    	String tLangString = Locale.getDefault().getLanguage();
    	
    	if (tLangString != null){
    		byte tLang[] = tLangString.getBytes();
    		
    		return IOTGetLanguageIntID(tLang, (short) tLang.length);
    	}
    	
    	return 1033;
    }
    
    /*
     * Push API functions
     */
    public boolean pushRegister(String aToken){
    	if (aToken != null){
    		byte tToken[] = aToken.getBytes();
    		long tSSID[] = {0};
    		
    		return isOK("pushRegister", IOTPushRegister(tToken, tToken.length, tSSID, EPUSH_PROTOCOL.EPUSH_PROTOCOL_XINGE, GetLanguageIntID()));
    	}
    	
    	return false;
    }
    
    public boolean pushUnregister(){
    	return isOK("pushUnregister", IOTPushUnregister());
    }
    
    public int pushAddDeviceTrigger(long aDevID, int aType, int aParam, long aTarget, String aDevName, String aTriggerName){
    	byte tDevName[] = null;
    	byte tTriggerName[] = null;
    	
    	if (aDevName != null){
    		tDevName = aDevName.getBytes();
    	}
    	if (aTriggerName != null){
    		tTriggerName = aTriggerName.getBytes();
    	}
    	
    	return IOTPushAddDeviceTrigger(aDevID, aType, aParam, aTarget, tDevName, (short)(tDevName != null ? tDevName.length : 0), tTriggerName, (short)(tTriggerName != null ? tTriggerName.length : 0));
    }
    
    public boolean pushRemoveDeviceTrigger(long aID){
    	return isOK("pushRemoveDeviceTrigger", IOTPushRemoveDeviceTrigger(aID));
    }
    
    public boolean pushRemoveDeviceAllTrigger(long aDevID){
    	return isOK("pushRemoveDeviceAllTrigger", IOTPushRemoveDeviceAllTrigger(aDevID));
    }
    
    public IOTDeviceTrigger pushQueryDeviceTrigger(long aID){
    	long tDevID[] = new long[1];
    	int tType[] = new int[1];
    	int tParam[] = new int[1];
    	long tTarget[] = new long[1];
    	byte tDevName[] = new byte[IOTNetAPIConst.KPUSH_DEVICE_NAME_MAX_LENGTH];
    	short tDevNameLen[] = new short[]{(short) tDevName.length};
     	byte tName[] = new byte[IOTNetAPIConst.KPUSH_TRIGGER_NAME_MAX_LENGTH];
     	short tNameLen[] = new short[]{(short) tName.length};
     	int tValid[] = new int[1];
     	int tEnable[] = new int[1];
   	
    	if(isOK("pushQueryDeviceTrigger", IOTPushQueryDeviceTrigger(aID, tDevID, tType, tParam, tTarget, tDevName, tDevNameLen, tName, tNameLen, tValid, tEnable))){
    		return new IOTDeviceTrigger(aID, tDevID[0], tType[0], tParam[0], tTarget[0], new String(tDevName, 0, tDevNameLen[0]), new String(tName, 0, tNameLen[0]), ISTRUE(tValid[0]), ISTRUE(tEnable[0]));
    	};
    	
    	return null;
    }
    
    public ArrayList<IOTDeviceTrigger> pushRetriveDeviceAllTrigger(long aDevID){
    	ArrayList<IOTDeviceTrigger> tTriggers = new ArrayList<IOTDeviceTrigger>();
    	long tID[] = new long[1];
    	int tType[] = new int[1];
    	int tParam[] = new int[1];
    	long tTarget[] = new long[1];
    	byte tDevName[] = new byte[IOTNetAPIConst.KPUSH_DEVICE_NAME_MAX_LENGTH];
    	short tDevNameLen[] = new short[]{(short) tDevName.length};
     	byte tName[] = new byte[IOTNetAPIConst.KPUSH_TRIGGER_NAME_MAX_LENGTH];
     	short tNameLen[] = new short[]{(short) tName.length};
     	int tValid[] = new int[1];
     	int tEnable[] = new int[1];
   	
    	while(isOK("pushRetriveDeviceTrigger", IOTPushRetriveDeviceTrigger(aDevID, tID[0], tID, tType, tParam, tTarget, tDevName, tDevNameLen, tName, tNameLen, tValid, tEnable))){
    		tTriggers.add(new IOTDeviceTrigger(tID[0], aDevID, tType[0], tParam[0], tTarget[0], new String(tDevName, 0, tDevNameLen[0]), new String(tName, 0, tNameLen[0]), ISTRUE(tValid[0]), ISTRUE(tEnable[0])));
    		
    		tDevNameLen[0] = (short) tDevName.length;
    		tNameLen[0] = (short) tName.length;
    	};
    	
    	if (tTriggers.size() > 0){
    		return tTriggers;
    	}else{
    		return null;
    	}
    }
}
